#pragma once 
#include <pthread.h>
#include <queue>
#include "Task.hpp"

const int gcap = 5;

template <class T>
class BlockQueue
{
public:
    BlockQueue(int cap = gcap) 
        : _cap(gcap)
    {
        pthread_mutex_init(&_mutex,nullptr);
        pthread_cond_init(&_Consumercond,nullptr);
        pthread_cond_init(&_productorcond,nullptr);
    }

    bool isEmpty()  {return _q.empty(); }
    bool isFull() { return _q.size() == gcap; }

    void push(const T &in)
    {
        pthread_mutex_lock(&_mutex);
        // 1.在临界区内部，走到这里我们一定持有锁
        // 2.要让线程进行休眠等待，不能持有锁！！！
        // 3.pthread_cond_wait就必须有能够释放锁的能力
        // 4.当线程醒来的时候，一定是从临界区醒来，因为是在临界区被切走的
        // 5.所以线程在唤醒后，要重新申请锁，才能从wait函数返回

        // 假如被错误唤醒了，但队列已经满了，不能继续生产，所以是while循环
        while (isFull())
        {
            pthread_cond_wait(&_productorcond, &_mutex);
        }

        // 可以生产
        _q.push(in);

        // 唤醒
        if(_q.size() >= _cap/2) 
            pthread_cond_signal(&_Consumercond);

        pthread_mutex_unlock(&_mutex);
    }

    void pop(T *out)
    {
        pthread_mutex_lock(&_mutex);

        while (isEmpty())
        {
            pthread_cond_wait(&_Consumercond, &_mutex);
        }

        // 可以消费
        *out = _q.front();
        _q.pop();

        // 唤醒
        // if(_q.size() <= _cap/2) 
            pthread_cond_signal(&_productorcond);

        pthread_mutex_unlock(&_mutex);
    }

    ~BlockQueue()
    {
        pthread_mutex_destroy(&_mutex);
        pthread_cond_destroy(&_Consumercond);
        pthread_cond_destroy(&_productorcond);
    }

private:
    std::queue<T> _q;
    int _cap;
    pthread_mutex_t _mutex;
    pthread_cond_t _Consumercond;
    pthread_cond_t _productorcond;
};
